package com.tre.jdevtemplateboot.common.util;

import com.tre.jdevtemplateboot.common.constant.SysConstantToken;
import com.tre.jdevtemplateboot.exception.business.SysInvalidTokenException;
import com.tre.jdevtemplateboot.exception.business.SysInvalidUserException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.Map;

/**
 * @program: jdev-boot-core
 * @description
 * @author: JDev
 * @create: 2021-07-30 08:27
 **/

public class JwtUtils {

    private JwtUtils() {
    }

    /**
     * 创建 token
     *
     * @param claims
     * @return
     */
    public static String createJwtToken(Claims claims) {
        Long expiration = Long.parseLong(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.token-expire"));
        return createJwt(claims, expiration);
    }

    /**
     * 创建 token
     *
     * @param claims
     * @param time   time < 0 token 不设置期限
     * @return
     */
    public static String createJwtToken(Claims claims, Long time) {
        return createJwt(claims, time);
    }

    /**
     * 创建 token
     *
     * @param claims
     * @return
     */
    public static String createJwtToken(Map<String, Object> claims) {
        Long expiration = Long.parseLong(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.token-expire"));
        return createJwt(claims, expiration);
    }

    /**
     * 创建 token
     *
     * @param claims
     * @param time
     * @return
     */
    public static String createJwtToken(Map<String, Object> claims, Long time) {
        return createJwt(claims, time);
    }

    /**
     * 解析Token
     *
     * @param token
     * @return
     */
    public static Claims getClaimsByToken(String token) {
        SecretKey key = generalKey();
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(token).getBody();
            Date expiration = claims.getExpiration();
            if (expiration != null && isTokenExpired(expiration)) {
                throw new SysInvalidTokenException();
            }

        } catch (Exception e) {
            throw new SysInvalidUserException();
        }
        return claims;
    }

    /**
     * 判断 token 是否过期
     *
     * @param expiration token过期日期时间
     * @return
     */
    public static boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }

    /**
     * 由字符串生成加密key
     *
     * @return
     */
    private static SecretKey generalKey() {
        String stringKey = YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.token-security-key");
        return new SecretKeySpec(stringKey.getBytes(), "AES");
    }

    /**
     * 创建JWT
     *
     * @param claims
     * @param time
     * @return
     */
    private static String createJwt(Claims claims, Long time) {

        if (!StringUtils.hasLength(claims.getSubject())) {
            claims.setSubject(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.pre-user-subject"));
        }
        if (!StringUtils.hasLength(claims.getIssuer())) {
            claims.setIssuer(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.pre-user-issuer"));
        }

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        Date now = new Date(System.currentTimeMillis());
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId(claims.getId())
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey);
        if (time > 0) {
            Date exp = new Date(nowMillis + time);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 创建JWT
     *
     * @param claims
     * @param time
     * @return
     */
    private static String createJwt(Map<String, Object> claims, Long time) {

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        Date now = new Date(System.currentTimeMillis());
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId((String) claims.get(SysConstantToken.USER_ID_KEY))
                .setSubject(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.pre-user-subject"))
                .setIssuer(YamlConfigurerUtil.getStrYmlVal("com.tre.jdev.pre-user-issuer"))
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey);
        if (time > 0) {
            Date exp = new Date(nowMillis + time);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

}
